package trinidad;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

/**
 * G��wny kontroler interakcji
 */
public class BattleField extends JPanel {
	private static final long serialVersionUID = 213223;
	
	private boolean screenBlocked;
	
	public static final int NEXTTURNBUTTON = 1;
		
	private final int ofs = 20;
	private final String path = "./grafx/";

	/** 
	 *  Graphics 
	 */
	private ImageIcon normalField, specialField, sField[];
	private ImageIcon poss;
	
	private int[][] possMatrix;
	
    /**
     *  Game controller
     */
	private GamePlay play;
	private BestResults bestResults;
	/**
	 * Control variables
	 */
	int bluePlayerMoves = 0;
	int violetPlayerMoves = 0;
	
	int selFieldX, selFieldY;
	int playerSelected;
	boolean playerLocked;
	private ArrayList<GameState> stateList;
	/**
	 * Associates
	 */
	JButton nextTurnButton;

	public boolean sieciowa;
	
	public String ip;
	
	public BattleField(int maxdepth, int overrun, String opponent, boolean reverseturns, boolean humanfirst, String ip, boolean sieciowa) {
		super();
		this.ip = ip;
		this.sieciowa = sieciowa;
		stateList = new ArrayList<GameState>();
		bestResults = new BestResults();
		screenBlocked = false;
		// poss layer matrix setup
		possMatrix = new int[7][7];
		
		// set selection variables
		selFieldX = -1; selFieldY = -1;
		playerSelected = 0;
		playerLocked = false;
				
		// load bitmap data
		normalField = new ImageIcon(path+"fn.jpg");
		specialField = new ImageIcon(path+"fsbig.jpg");
		sField = new ImageIcon[4];
		sField[0] = new ImageIcon(path+"fdark.png");
		sField[1] = new ImageIcon(path+"fligh.png");
		sField[2] = new ImageIcon(path+"fwhite.png");
		sField[3] = new ImageIcon(path+"fwhitev.png");
		poss = new ImageIcon(path+"poss.png");
		
		// battlesetup
		int enemy = GamePlay.HUMAN;
		if (opponent.equalsIgnoreCase("alfazabojca")) enemy = GamePlay.ALFABETAZABOJCA;
		if (opponent.equalsIgnoreCase("powerbeta")) enemy = GamePlay.POWERBETA;
		if (opponent.equalsIgnoreCase("underttaker")) enemy = GamePlay.UNDERTTAKER;
		if (opponent.equalsIgnoreCase("maddogmtd")) enemy = GamePlay.MADDOG;
		if (opponent.equalsIgnoreCase("emptinessmurderess")) enemy = GamePlay.EMDA;
		if (opponent.equalsIgnoreCase("net")) enemy = GamePlay.NET;
		
		if (humanfirst) play = new GamePlay(GamePlay.HUMAN, enemy, reverseturns, maxdepth, overrun);
		else play = new GamePlay(enemy, GamePlay.HUMAN, reverseturns, maxdepth, overrun);
		
		if(this.sieciowa){
			if(humanfirst){
				((NetPlayer)play.plbOut).setIp(this.ip);
			}
			else {
				((NetPlayer)play.plaOut).setIp(this.ip);
			}
		}
		
		// rusz si� jak trzeba
		
		if (enemy != GamePlay.HUMAN && !humanfirst) {
			play.turnSwitch();
			makaMuva();
		}
		
		// Grab mouseclicks and pass it to fieldAction
		this.addMouseListener(new MouseAdapter(){
			public void mousePressed(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					int x = (e.getX() - ofs) / 40;
					int y = (e.getY() - ofs) / 40;
					if (x >= 0 && x < 7 && y >= 0 && y < 7)
						fieldActionClick(x, y);
				}
				e.consume();
			}
		});
		
	}
	
	public void join(int type, Object token) {
		if (type == NEXTTURNBUTTON) nextTurnButton = (JButton) token;
	}
	
	public void makaMuva() {
		selFieldX = -1;
		selFieldY = -1;		
		if(winnerAnnouced()) {
			return;
		}
		// refreszu �eby by�o kul
		possMake();
		this.update(this.getGraphics());
		Move[] mx = play.turnSwitch();
		
		if (mx != null) {
			for (int a = 0; a < mx.length; a++) {
				play.forceTransition(mx[a]);
			}
			countMoves();
			play.turnSwitch();
			addStateToList();
			play.whoseMove = GamePlay.HUMAN_MOVE;
		}
		if(winnerAnnouced()) {
			return;
		}
		playerLocked = false;
	}
	
	private boolean winnerAnnouced() {
		int gss = play.checkGameState();
		if (gss != 0) {
			String gssT = "";
			int moves = 0;
			if (gss == 1) {
				gssT = "niebieski";
				moves = this.bluePlayerMoves;
			} else {
				gssT = "fioletowy";
				moves = this.violetPlayerMoves;
			}
			//System.out.println("Zwyciężył gracz " + gssT + "!");
			//System.out.println("Wykonal " + moves + " ruchów");
			screenBlocked = true;
			repaint();
			if ( (play.whoseMove == GamePlay.COMPUTER_MOVE)
			   ||(play.plaType == GamePlay.HUMAN && play.plbType == GamePlay.HUMAN)
			) {
				if( (bestResults.getWorstResult() > moves)
				 || (bestResults.getResultsNumber() < BestResults.NUMBER_OF_RESULTS)) {
					String inputValue = (String)JOptionPane.showInputDialog(this,"Proszę wprowadź swoje imię:","Gratulacje, wygrałeś!",JOptionPane.INFORMATION_MESSAGE,null,null,"masta");
					bestResults.addResult(moves, inputValue);
					JOptionPane.showMessageDialog(null,bestResults.toString(),"Lista najlepszych",JOptionPane.INFORMATION_MESSAGE);
				}
				else {
					JOptionPane.showMessageDialog(null,"Zwyciężył gracz " + gssT + "!\nGratulacje!","Koniec gry",JOptionPane.INFORMATION_MESSAGE);				
				}
			} else {
				JOptionPane.showMessageDialog(null,"Zwyciężył komputer :(","Koniec gry",JOptionPane.INFORMATION_MESSAGE);
			}
			return true;
		}
		return false;
	}
	
	/* player tells that he has finished moving */
	public void imDone() {
		makaMuva();
		possMake();
		nextTurnButton.setEnabled(playerLocked);
		repaint();
	}

	private void possMake() {
		for (int a = 0; a < 7; a++)
			for (int b = 0; b < 7; b++)
				possMatrix[a][b] = 0;
		if (selFieldX != -1) {
			Move[] moves = play.getCurrentState().generatePossibleMoves((byte)selFieldX, (byte)selFieldY, playerLocked);
			for (int i = 0; i < moves.length; i++) possMatrix[moves[i].ex][moves[i].ey] = 1;
		}
	}
	
	/**
	 * On click action (specific field)
	 * @param x
	 * @param y
	 * TODO Check whose turn
	 */
	public void fieldActionClick(int x, int y) {
		if (screenBlocked) return;
		if (playerSelected == 0) {
			if (play.getCurrentState().getState(x, y) == (play.getTurn() + 1)) {
				selFieldX = x;
				selFieldY = y;
			} 
		} else {
			int eta;
			if ((eta = play.getCurrentState().secureTrans((byte)selFieldX, (byte)selFieldY, (byte)x, (byte)y, playerLocked)) == GameState.FAULT) {
				if (!playerLocked && play.getCurrentState().getState(x, y) == (play.getTurn() + 1)) {
					selFieldX = x;
					selFieldY = y;				
				}
			} else {
				if (eta == GameState.PARTIAL) {
					selFieldX = x;
					selFieldY = y;
					playerLocked = true;
				} else {
					if(this.sieciowa){
						if(play.getTurn()==0){
							System.out.println("slucham na porcie: 5555");
							((NetPlayer)play.plbOut).setServerSocket("5555");
							((NetPlayer)play.plbOut).send(play.getCurrentState(),this.ip,"5555");
						} else {
							System.out.println("slucham na porcie: 4444");
							((NetPlayer)play.plaOut).setServerSocket("4444");
							((NetPlayer)play.plaOut).send(play.getCurrentState(),this.ip,"4444");
						}
						System.out.println("Wysłalem");
						System.out.println(play.getCurrentState().getString());
						selFieldX = -1; 
						selFieldY = -1;
						for (int a = 0; a < 7; a++)
							for (int b = 0; b < 7; b++)
								possMatrix[a][b] = 0;
						this.update(this.getGraphics());
						this.validate();
						this.repaint();						
					}
					countMoves();

					makaMuva();
					
					addStateToList();
					play.whoseMove = GamePlay.COMPUTER_MOVE;
				}
			}
		}
		possMake();
		nextTurnButton.setEnabled(playerLocked);
		repaint();
	}
	
	private void countMoves() {
		if(this.play.getTurn() == 0) {
			this.bluePlayerMoves++;
			//System.out.println("Niebieski: "+bluePlayerMoves);
		} else if(this.play.getTurn() == 1) {
			this.violetPlayerMoves++;
			//System.out.println("Fioletowy: "+violetPlayerMoves);
		}
	}
	
	private void addStateToList() {
		if(this.stateList.size() == 0) {
			this.stateList.add(new GameState());
		}
		this.stateList.add(new GameState(this.play.getCurrentState()));
		//System.out.println("Dodano stan o indeksie: " + (this.stateList.size() - 1));
	}
	
	public void undoState() {
		if(this.stateList.size() >= 3) {
			this.play.setCurrentState(new GameState(this.stateList.get(this.stateList.size()-3)));
			this.stateList.remove(this.stateList.size()-1);
			this.stateList.remove(this.stateList.size()-1);
			if(screenBlocked) {
				screenBlocked = false;
			}
			selFieldX = -1; 
			selFieldY = -1;
			for (int a = 0; a < 7; a++)
				for (int b = 0; b < 7; b++)
					possMatrix[a][b] = 0;
			this.update(this.getGraphics());
			this.validate();
			this.repaint();
		}
	}
	
	public boolean canUndoState() {
		if (this.stateList.size() >= 3) {
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * Draws GameBoard
	 * @param g
	 * @param dimX
	 * @param dimY
	 * 
	 * TODO additional params - move possibilities 
	 */
	public void drawBoard(Graphics g) {
		specialField.paintIcon(this, g, ofs + 2*40, ofs + 2*40);		
		for (int a = 0; a < 7; a++)
			for (int b = 0; b < 7; b++) { 
				if (a < 2 || a > 4 || b < 2 || b > 4)
					normalField.paintIcon(this, g, ofs + a*40, ofs + b*40);
				if (possMatrix[a][b] == 1) poss.paintIcon(this, g, ofs + a*40, ofs + b*40);
			}
	}
	
	/**
	 * Draws current state
	 * @param g
	 * @param state
	 */
	public void drawState(Graphics g,GameState state) {
		playerSelected = 0;
		for (int a = 0; a < 7; a++)
			for (int b = 0; b < 7; b++) {
				int addFactor = 0;
				if (selFieldX == a && selFieldY == b) addFactor = 2;
				switch (state.getState(a,b)) {					
				    case 0 : break;
					case 1 : {
						sField[0 + addFactor].paintIcon(this, g, ofs + a*40, ofs + b*40);
						if (addFactor == 2) playerSelected = 1;
						break;
					}
					case 2 : {
						sField[1 + addFactor].paintIcon(this, g, ofs + a*40, ofs + b*40);
						if (addFactor == 2) playerSelected = 2;
						break;						
					}
				}
			}
	}
	
	/**
	 * Performs visualisation
	 * @param g GC
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		drawBoard(g);
		drawState(g, play.getCurrentState());		
	}
	
	/**
	 * Drawing redirect
	 */
	public void draw() {
		repaint();
	}		
}
